
__metaclass__ = type

import os
import sys
import time
import signal
import threading
import subprocess

RELOADER_CODE = 125

def watch(f):
    Watcher.extra.append(os.path.abspath(f))


def wait():
    Watcher().start()


def run(key):
    exe = sys.executable
    args = [exe] + sys.argv

    while True:
        proc = None
        environ = os.environ.copy()
        environ[key] = 'true'
        try:
            signal.signal(signal.SIGTERM, _handle_sigterm)
            proc = subprocess.Popen(args, env=environ)
            status = proc.wait()
            proc = None

        except KeyboardInterrupt:
            sys.stderr.write('\nStopping\n')
            sys.stderr.flush()
            return 1

        finally:
            if proc is not None and hasattr(os, 'kill'):
                try:
                    os.kill(proc.pid, signal.SIGTERM)
                except (OSError, IOError):
                    pass

        if status != RELOADER_CODE:
            return status



def _handle_sigterm(signo, frame):
    raise SystemExit



class Watcher(threading.Thread):

    extra = []

    def _set_daemon(self):
        return True

    def run(self, interval=1):
        self.mtimes = {}
        self.changed = None
        while self._check():
            time.sleep(interval)
        sys.stderr.write('\n%s changed\n' % self.changed)
        os._exit(RELOADER_CODE)

    def _files(self):
        for m in sys.modules.values():
            f = getattr(m, '__file__', None)
            if f:
                yield f.endswith('.pyc') and f[:-1] or f
        for f in self.extra:
            yield f

    def _check(self):
        for f in self._files():
            try:
                stat = os.stat(f)
                mtime = stat and stat.st_mtime or 0
            except (OSError, IOError):
                continue
            if f not in self.mtimes:
                self.mtimes[f] = mtime
            elif self.mtimes[f] < mtime:
                self.changed = f
                return False
        return True





